#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import abc
import io, os
from array import array

class ReadBuffer:
    @abc.abstractmethod
    def __init__ (self): ...
    @abc.abstractmethod
    def __del__ (self): ...
    @abc.abstractmethod
    def __len__ (self): ...
    @abc.abstractmethod
    def __iter__ (self): ...
    @abc.abstractmethod
    def __next__ (self): ...
    @abc.abstractmethod
    def __enter__ (self): ...
    @abc.abstractmethod
    def __exit__ (self, exc_type, exc_value, traceback): ...
    @abc.abstractmethod
    def tell (self): ...
    @abc.abstractmethod
    def seek (self, base, offset): ...
    @abc.abstractmethod
    def read (self): ...
    @abc.abstractmethod
    def peek (self, size): ...
    @abc.abstractmethod
    def eof (self): ...
    @abc.abstractmethod
    def skipuntil (self, delimiter: int): ...
    @abc.abstractmethod
    def skipuntilnot (self, delimiter: int):...
    @abc.abstractmethod
    def _close (self): ...

class UnicodeReadBuffer(ReadBuffer):
    __slots__ = ("_data", "_pos")
    def __init__ (self, data):
        if isinstance(data, str):
            self._data = array("u", data)
        elif isinstance(data, bytes):
            self._data = array("u", data.decode("utf-8"))
        elif isinstance(data, bytearray):
            self._data = array("u", data.decode("utf-8"))
        elif isinstance(data, array):
            self._data = data
        else:
            raise TypeError("data must be str, bytes, bytearray, or array")
        self._pos = 0
    def __del__ (self):
        self._close()
    def __len__ (self):
        return len(self._data)
    def __getitem__ (self, index):
        if index < 0 or index >= len(self._data):
            raise IndexError("index out of range")
        return ord(self._data[index])
    def __iter__ (self):
        return self
    def __next__ (self):
        if self._pos >= len(self._data):
            raise StopIteration
        self._pos += 1
        return ord(self._data[self._pos - 1])
    def __enter__ (self):
        return self
    def __exit__ (self, exc_type, exc_value, traceback):
        self._close()
    def eof (self):
        return self._pos >= len(self._data)
    def tell (self):
        return self._pos
    def seek (self, base = os.SEEK_SET, offset = 0):
        if base == os.SEEK_SET:
            if offset < 0 or offset > len(self._data):
                raise ValueError("offset out of range")
            self._pos = offset
        elif base == os.SEEK_CUR:
            if self._pos + offset < 0 or self._pos + offset > len(self._data):
                raise ValueError("offset out of range")
            self._pos += offset
        elif base == os.SEEK_END:
            if offset < -len(self._data) or offset > 0:
                raise ValueError("offset out of range")
            self._pos = len(self._data) + offset
        else:
            raise ValueError("invalid base")
    def read (self):
        if self._pos >= len(self._data):
            return -1
        self._pos += 1
        return ord(self._data[self._pos - 1])
    def peek (self, size = 1):
        if self._pos + size > len(self._data):
            return -1
        return ord(self._data[self._pos + size - 1])
    def skipuntil (self, delimiter):
        for i in self:
            if i == delimiter:
                return
    def skipuntilnot (self, delimiter):
        for i in self:
            if i != delimiter: return
    def _close (self):
        self._data = None
        self._pos = 0
